﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;

using ABCat.Core.Plugins.DataSets;

using SQLite.Net;
using SQLite.Net.Platform.Win32;

namespace ABCat.Plugins.DataSources.AudioBooks
{
	public class BinaryDataSet : SQLiteConnection, IBinaryDataSet
	{
		#region + Fields +

		private readonly ConcurrentQueue<BinaryData> _addedBinaryData = new ConcurrentQueue<BinaryData>();
		private readonly ConcurrentQueue<BinaryData> _changedBinaryData = new ConcurrentQueue<BinaryData>();
		private readonly object _lockContext;

		private volatile bool _isTablesCreated;

		#endregion
		#region + Ctor +

		public BinaryDataSet(string databasePath, object lockContext)
			: base(new SQLitePlatformWin32(), databasePath)
		{
			_lockContext = lockContext;

			if (!_isTablesCreated)
			{
				lock (_lockContext)
				{
					if (!_isTablesCreated)
					{
						CreateTable<BinaryData>();
						_isTablesCreated = true;
					}
				}
			}

			//DeleteAllPages();

			//if (databasePath != "D:\\meta.sqlite")
			//{
			//	ExportMetaData("D:\\meta.sqlite");
			//}
		}

//		private void DeleteAllPages()
//		{
//			var keys = GetKeysAll().Where(item => item.StartsWith("TP_")).ToArray();

//			RunInTransaction(() => Execute(@"
//			DELETE FROM BinaryData
//			WHERE Key IN({0})".F(string.Join(",", keys))));
//		}

		//private void ExportMetaData(string filePath)
		//{
		//	var keys = GetKeysAll().Where(item => item.StartsWith("TPM_")).ToArray();
		//	using (var binaryDataSet = new BinaryDataSet(filePath, _lockContext))
		//	{
		//		var data = new List<BinaryData>();
		//		var partKeys = new HashSet<string>();

		//		for (int z = 0; z < keys.Length; z++)
		//		{
		//			partKeys.Add(keys[z]);

		//			if (partKeys.Count == 500 || z == keys.Length - 1)
		//			{
		//				var binData = GetByKeys(partKeys).ToArray();

		//				foreach (var binaryData in binData)
		//				{
		//					binaryData.SetData(binaryData.Data, true);
		//				}

		//				binaryDataSet.AddChangedBinaryData(binData);
		//				binaryDataSet.SaveBinaryData();
		//				partKeys.Clear();
		//			}
		//		}
		//	}
		//}

		#endregion
		#region + Logic +

		public void AddBinaryData(IBinaryData binaryData)
		{
			_addedBinaryData.Enqueue((BinaryData)binaryData);
		}

		public void AddChangedBinaryData(params IBinaryData[] audioBooks)
		{
			foreach (var binaryData in audioBooks.Cast<BinaryData>())
			{
				_changedBinaryData.Enqueue(binaryData);
			}
		}

		public IBinaryData CreateBinaryData()
		{
			return new BinaryData();
		}

		public IBinaryData GetByKey(string key)
		{
			lock (_lockContext)
			{
				return Table<BinaryData>().Where(row => row.Key == key).FirstOrDefault();
			}
		}

		public IEnumerable<IBinaryData> GetByKeys(HashSet<string> keys)
		{
			lock (_lockContext)
			{
				return Table<BinaryData>().Where(row => keys.Contains(row.Key));
			}
		}

		public void SaveBinaryData()
		{
			lock (_lockContext)
			{
				var data4Add = new List<BinaryData>();

				while (_addedBinaryData.Count > 0)
				{
					BinaryData data;
					if (_addedBinaryData.TryDequeue(out data))
					{
						data4Add.Add(data);
					}
				}

				var data4Replace = new List<BinaryData>();

				while (_changedBinaryData.Count > 0)
				{
					BinaryData data;
					if (_changedBinaryData.TryDequeue(out data))
					{
						data4Replace.Add(data);
					}
				}

				RunInTransaction(() =>
				{
					if (data4Replace.AnySafe())
					{
						Execute(@"
			DELETE FROM BinaryData
			WHERE Key IN({0})".F(string.Join(",", data4Replace.Select(item => "'{0}'".F(item.Key)))));
					}
					InsertAll(data4Add.Union(data4Replace));
				});
			}
		}

		public HashSet<string> GetKeysAll()
		{
			return new HashSet<string>(Query(Table<BinaryData>().Table, "SELECT Key FROM BinaryData").Cast<BinaryData>().Select(item=>item.Key));
		}

		public void Import(string dbFilePath)
		{
			var existed = GetKeysAll();

			using (var binaryDataSet = new BinaryDataSet(dbFilePath, _lockContext))
			{
				var importKeys = binaryDataSet.GetKeysAll();

				var newKeys = importKeys.Where(item => !existed.Contains(item)).ToArray();
				var currentKeys = new HashSet<string>();

				for (int z = 0; z < newKeys.Length; z++)
				{
					currentKeys.Add(newKeys[z]);

					if (currentKeys.Count == 20 || z == newKeys.Length - 1)
					{
						AddChangedBinaryData(binaryDataSet.GetByKeys(currentKeys).ToArray());
						SaveBinaryData();
					}
				}
			}
		}

		#endregion
	}
}